﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved All Rights Reserved All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Threading;

namespace FinPlusUtility
{
    public static class Utility
    {
        public class Config : Dictionary<string, Dictionary<string, string>> { }
        public enum Env { dev, test, uat, prod, unknown }

        //configs and settings
        public static Config ConfigXml(string path, out Env environment)
        {
            var doc = new XmlDocument();

            doc.Load(path);
            var res = new Config();

            var env = doc.SelectSingleNode("configuration/environment").Attributes["name"].Value;
            var config = doc.SelectNodes("configuration/" + env);
            if (!Enum.TryParse(env, out environment))
                throw new Exception("!config environment " + env + " invalid should be dev, uat or prod");

            foreach (XmlElement settings in config)
            {
                foreach (XmlElement setting in settings)
                {
                    var d = res[setting.Name] = new Dictionary<string, string>();
                    foreach (XmlElement value in setting)
                        d[value.Attributes["name"].Value] = value.Attributes["value"].Value;
                }
            }
           
            return res;
        }

        public static Config UserSettingsRegistry(string path, string name)
        {
            //TODO
            return new Config();
        }

        public static Config UserSettingsDatabase(string path, string name)
        {
            //TODO
            return new Config();
        }

        public static string XmlTryParse(XmlElement element, string name, string defaultValue = "")
        {
            XmlNode n = element.Attributes[name];
            if (n == null) return defaultValue;
            return n.Value;
        }

        public static XmlAttribute XmlAttribute(XmlDocument doc, string name, string value)
        {
            var attribute = doc.CreateAttribute(name);
            attribute.Value = value;

            return attribute;
        }

        public static Assembly GetReferencedAssembly(Assembly executimgAssembly, string name)
        {
            var n = name.ToLower();
            foreach (var s in executimgAssembly.GetReferencedAssemblies())
                if (s.Name.ToLower() == n)
                    return Assembly.Load(s);

            return null;
        }

        public static string GetAssemblyPath(Assembly assembly)
        {
            return assembly.Location.Replace(assembly.ManifestModule.Name, string.Empty).ToLower();
        }

        public static Assembly[] GetAssemblys(string path,string[] assemblys)
        {
            var res = new List<Assembly>();
            foreach (var assembly in assemblys)
                res.Add(Assembly.LoadFile(string.Format("{0}{1}.dll",  path, assembly)));

            return res.ToArray();
        }

        public static string GetConfigPath(Assembly assembly)
        {
            return assembly.Location.ToLower().Replace(".exe", ".config.xml").Replace(".dll", ".config.xml");
        }

        public static string ThreadId()
        {
            return Thread.CurrentThread.ManagedThreadId.ToString();
        }

        public static int GetNumberOfDp(double value)
        {
            var res = value.ToString().Split('.');
            if (res.Count() < 2)
                return 0;

            return res[1].Length;
        }

        public static T EnumTryParse<T>(this string stringValue, T defaultValue)
        {
            if (!Enum.IsDefined(typeof(T), stringValue))
                return defaultValue;

            return (T)Enum.Parse(typeof(T), stringValue);
        }

        public static bool TryCast<T>(this object obj, T defaultValue, out T result)
        {
            try
            {
                result = (T)obj;
                return true;
            }
            catch
            {
                result = defaultValue;
                return false;
            }
        }

        public static string StringFromArray2d(object[,] array, char delimiterRow = '%', char delimiterCol = '$')
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                int rows = array.GetUpperBound(0);
                int cols = array.GetUpperBound(1);

                for (int i = 0; i <= rows; i++)
                {
                    for (int j = 0; j <= cols; j++)
                    {
                        sb.Append(array[i, j]);
                        if (i == rows && j == cols) continue;
                        sb.Append(j == cols ? delimiterRow : delimiterCol);
                    }
                }
                return sb.ToString();
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public static object StringToArray2d(string str, char delimiterRow = '%', char delimiterCol = '$')
        {
            try
            {
                string[] rows = str.Split(delimiterRow);
                string[,] array = null; 

                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    string[] cols = rows[i].Split(delimiterCol);
                    if (array == null) array = new string[rows.GetLength(0), cols.GetLength(0)];

                    for (int j = 0; j < cols.GetLength(0); j++)
                        array[i, j] = cols[j];
                }
                return array;
            }
            catch (Exception e)
            {
                return new string[,] { { "Error" }, { e.Message } };
            }
        }

        public static string StringFromArray1d(object[] array, char delimiter = '%')
        {
            StringBuilder sb = new StringBuilder();
            //object
            var u1 = array.GetUpperBound(0);
         
            for (int i = 0; i <= u1; i++)
            {
                if (i > 0) sb.Append(delimiter);

                sb.Append(array[i]);
            }
            return sb.ToString();
        }

        public static string[][] ToJaggedArray(string str, char delimiterRow = '\n', char delimiterCol = ',')
        {
            try
            {
                return str.Split(delimiterRow).Select(i => i.Split(delimiterCol).ToArray()).ToArray();
            }
            catch 
            {
                return null;
            }
        }
       
        public static object[] StringToArray1d(string str, char delimiter = '%')
        {
            object[] objArr = null;
            int i = 0;
            var arr1d = str.Split(delimiter);
            objArr = new object[arr1d.Length];

            foreach (var a1 in arr1d)
            {
                objArr[i] = a1;
                i++;
            }
            return objArr;
        }

        public static string[] Split(string str, char delimiter=',', string remove = @"\t|\n|\r| ", string replace = "")
        {
            return Replace(str, remove, replace).Split(delimiter);
        }

        public static IDictionary<string, string> ToDictionary(string str, bool isKeyFirst = true, char delimiter1 = ';', char delimiter2 = ':', string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                if (string.IsNullOrEmpty(str) || str.Equals("NA"))
                    return new Dictionary<string, string>();

                if(isKeyFirst)
                    return Split(str.TrimEnd(delimiter1), delimiter1, remove, replace).ToDictionary(f => f.Split(delimiter2)[0], f => f.Split(delimiter2)[1]);

                return Split(str, delimiter1, remove, replace).ToDictionary(f => f.Split(delimiter2)[1], f => f.Split(delimiter2)[0]);
            }
            catch
            {
                return null;
            }
        }

        public static object Array1dToArray2d(object[] array)
        {
            var r = new object[1, array.Length];

            var i = 0;
            foreach (var o in array)
            {
                r[0,i] = array[i];
                i++;
            }
            return r;
        }

        public static object ArrayTranspose(object[] array)
        {
            var r = new object[array.Length, 1];

            var i = 0;
            foreach (var o in array)
            {
                r[i, 0] = array[i];
                i++;
            }
            return r;
        }

        public static object StringWithInfoToObject(string str, string InfoType, char delimiterInfo = '|', char delimiterRow = '%', char delimiterCol = '$')
        {
            var s = str.Split(delimiterInfo);
            var val = s[s.GetUpperBound(0)];

            switch (InfoType)
            {
                case "String": return val;
                case "Number": return double.Parse(val);
                case "Array1D": return StringToArray1d(val);
                case "Array2D": return StringToArray2d(val);
            }
            return "#info type not recognised";
        }

        public static string ReplaceFirstOccurrence(string source, string find, string replace)
        {
            int Place = source.IndexOf(find);
            string result = source.Remove(Place, find.Length).Insert(Place, replace);
            return result;
        }

        public static string ReplaceLastOccurrence(string source, string find, string replace)
        {
            int Place = source.LastIndexOf(find);
            string result = source.Remove(Place, find.Length).Insert(Place, replace);
            return result;
        }

        public static string Replace(string value, string remove = @"\t|\n|\r| ", string replace = "")
        {
            return Regex.Replace(value, remove, replace);
        }

        public static string Replace(string value, IDictionary<string, string> replace)
        {
            if (replace != null && value.StartsWith("[") && value.EndsWith("]") && replace.ContainsKey(value))
                value = replace[value];
            else if (replace != null && replace.Count > 0)
                foreach (var val in replace)
                    if (val.Key.StartsWith("[") && val.Key.EndsWith("]") && value.Contains(val.Key))
                        value = ReplaceLastOccurrence(value, val.Key, val.Value);
                        
            return value;
        }

        public static DateTime NextWorkDay(DateTime date, int add)
        {
            var resDate = date;
            int found = 0;
            int step = add > 0 ? 1 : -1;
            while (Math.Abs(found) <  Math.Abs(add))
            {
                resDate = resDate.AddDays(step);
                if (resDate.DayOfWeek != DayOfWeek.Sunday && resDate.DayOfWeek != DayOfWeek.Saturday)
                    found++;
            }
            return resDate;
        }

        public static DateTime ParseDateTime(string dateTime)
        {
            DateTime res1 = DateTime.MaxValue;
            if (dateTime.Equals("Today") || dateTime.Equals("T"))
                return DateTime.Now.Date;

            if (dateTime.Equals("Yesterday") || dateTime.Equals("T-1"))
                return NextWorkDay(DateTime.Now.Date, -1);

            if (DateTime.TryParse(dateTime, out res1))
                return res1;

            double res2;
            if (double.TryParse(dateTime, out res2))
                return DateTime.FromOADate(res2);

            return res1;
        }

        public static string FormatDateTime(string value, string dateFormat = "dd-MMM-yyyy", string dateFomatLong = "dd-MMM-yyyy  HH:mm:ss")
        {
            if (value.ToString().Equals("Unbooked"))
                return value;

            var dt = DateTime.FromOADate(double.Parse(value));

            if (dt.TimeOfDay.TotalSeconds == 0)
                return dt.ToString(dateFormat);

            return dt.ToString(dateFomatLong);
        }

        public static double DoubleParse(string value, double defaultValue = 0)
        {
            if (value == null) return defaultValue;

            double.TryParse(value, out defaultValue);

            return defaultValue;
        }

        public static int IntParse(string value, int defaultValue = 0)
        {
            if (value == null) return defaultValue;

            int.TryParse(value, out defaultValue);
            
            return defaultValue;
        }

        public static string Guid()
        {
            return System.Guid.NewGuid().ToString().Replace("$","£").Replace("%","^");
        }
    }
}
